import java.io.*;

/**
 * Creates a Command-Line User Interface(CLI) for a Client that displays provided input and captures user input
 * for outputing to the ClientHandler.
 * @author BluJaded Team
 * @version (see Sourceforge.net)
 */
public class ClientCmdLnUI implements Runnable, ClientUiInterface {
    
    // reference for the Client object assoiated to this CientCommandLineInterface
    private Client client;
    
    // a switch variable used to terminate the thread cleanly
    private boolean run = true;
    
    // a BufferedReader to read user input from teh command line (System.in)
    private BufferedReader input;
    
    // Thread variable to hold the thread of the CLI that handles the input from the CL
    private Thread uiListener;
    
    // boolean value used to denote if the client is attempting a wisper using the /w <nickname> <message> command
    private boolean attemptWisp;
    
    // boolean value used to denote if the result from a attempting to wisper using the /w <nickname> <message> command has been recieved
    private boolean resultRecieved;
    
    /**
     * Creates the CLI for a Client object provided.
     *
     * @param myClient the Client object assosiated with this GUI
     */
    public ClientCmdLnUI(Client myClient){
    //saves the Client object provided to a local variable    
    client=myClient;
    //initialises the BufferedReader on the System.in Inputstream to recieve user input from the CL
    input = new BufferedReader(new InputStreamReader(System.in));
    //creates a new thread assosiated with this object
    uiListener = new Thread (this);
    //starts the created Thread
    uiListener.start ();  
    }
    
    /**
     * Run method required by the implementation of Runnable is executed when a new thread, once created, is started.
     * Sets up an loop that sends upon reciept, any message it recieves from its BufferedReader until terminated or
     * an exception occurs.
     */
    public void run(){
        
        while (run){
            try{
                
                String inputedText=input.readLine();
                
                //splits the inputed text into its composit words and adds these words to a string array
                //a word being anything surrounded by whitespaces
                //the first 3 words are placed in the array and the forth element of the array holds
                //the remainder of the inputed text
                //if the string begins with one or more whitespaces then the first element will hold an empty string
                String[] inpt = inputedText.split("\\s+",4);
                
                //if the inputed text was an empty string then inpt will be an empty array
                //if inpt is empty
                if (inpt.length==0){
                    //makes inpt an array with a single empty string so that it can be proccessed bellow
                    inpt = new String[]{""};
                }
                
                //if the first element of the inpt array is an empty string and there is more than one element in the array
                if (inpt[0].equals("") && inpt.length>1) {
                    
                    //creates a new string array with one less element than the inpt array
                    String[] inptcopy= new String[inpt.length-1];
                    
                    //copies each but the first element of inpt into the new string array
                    for(int i = 1;i < inpt.length;i++){
                        inptcopy[i - 1] = inpt[i];
                    }
                    
                    //passes the new shortened array to the inpt array
                    inpt=inptcopy;
                }
                
                //if there was a message in the textField
                if (!inpt[0].equals("")) {
                    //creates a new Message object
                    Message sndMsg = new Message();
                    
                    //if the read line is a list command
                    if (inpt[0].equals("/list")) {
                        //make the Message a list command
                        sndMsg.setListClients(true);
                        //sends the message to the ClientHandler
                        client.outputMsg( sndMsg );
                    } 
                    else{
                        //if the read line is a quit command
                        if(inpt[0].equals("/quit")) {
                            //terminates the clients thread cleanly
                            client.terminateClient();
                            //make the Message a quit command
                            sndMsg.setQuit(true);
                            //sends the message to the ClientHandler
                            client.outputMsg( sndMsg );
                            //terminates this CLI's thread cleanly
                            run=false;
                        } 
                        else{
                            //if the read line is a quitserver command
                            if(inpt[0].equals("/quitserver")) {
                                //make the Message a quitserver command
                                sndMsg.setServerQuit(true);
                                //sends the message to the ClientHandler
                                client.outputMsg( sndMsg );
                            } 
                            else{
                                
                                if (inpt[0].equals("/b")){
                                    sndMsg.setEndWisp(true);
                                    //sends the message to the ClientHandler
                                    client.outputMsg( sndMsg );;
                                } 
                                else{
                                    if (inpt[0].equals("/help")){
                                        sndMsg.setHelp(true);
                                        //sends the message to the ClientHandler
                                        client.outputMsg( sndMsg );
                                    } 
                                    else{
                                        //if the message is more than a single word
                                        if (inpt.length>=2){
                                            
                                            //if the second word is not an empty string
                                            if (!inpt[1].equals("")){
                                                //if the first word of the message is a nickname change command
                                                if (inpt[0].equals("/nick")){
                                                    //if the provided nickname(the next word) is less than 10 characters
                                                    if (inpt[1].length()<10){
                                                        //set the messages nickname variable to the provided nickname
                                                        sndMsg.setNick(inpt[1]);
                                                    } 
                                                    else{
                                                        //set the messages nickname variable to the first 10 characters of the provided nickname
                                                        sndMsg.setNick(inpt[1].substring(0,10));
                                                    }
                                                    
                                                    //if the provided nickname is not this Clients Nickname
                                                    if (!sndMsg.getNick().toLowerCase().equals(client.getNickname().toLowerCase())){
                                                        // Write message to outputstream of client
                                                        client.writeOutput(sndMsg);
                                                    }
                                                } 
                                                else{
                                                    
                                                    //if the first word of the message is a whisper command
                                                    if(inpt[0].equals("/w")){
                                                        String toNick;
                                                        
                                                        //if the provided nickname(the next word) is less than 10 characters
                                                        if (inpt[1].length()<10){
                                                            //set the toNick variable to the provided nickname
                                                            toNick = inpt[1];
                                                        } 
                                                        else{
                                                            //set the toNick variable to the first 10 characters of the provided nickname
                                                            toNick = inpt[1].substring(0,10);
                                                        }
                                                        //it the specified nickname is not this Clients Nickname
                                                        if (!toNick.toLowerCase().equals(client.getNickname().toLowerCase())){
                                                            
                                                            //if the message is more than 2 words
                                                            if (inpt.length>2){
                                                                
                                                                //set the attemptWisp flac to denote that the client is attempting a wisper
                                                                attemptWisp=true;
                                                                
                                                                //sets the Messages wisp variable to true which is used to suppress mode change conformation
                                                                sndMsg.setWisp(true);
                                                                //set the Messages MsgTo variable to the provided nickname, acting as a wisper command to the ClientHandler
                                                                sndMsg.setMsgTo(toNick);
                                                                //sends the message to the ClientHandler
                                                                client.outputMsg(sndMsg);
                                                                
                                                                int timer=0;
                                                                //while the result from an attempt to wisper using the /w <nickname> <message> command has not been recieved
                                                                while (!resultRecieved){
                                                                    //increments the timer int
                                                                    timer++;
                                                                    //it the timer int has reached its timeout value
                                                                    if (timer>2000000000){
                                                                        //terminate this while loop
                                                                        resultRecieved=true;
                                                                        //mark the wisper attempt as having failed
                                                                        attemptWisp=false;
                                                                        
                                                                        //make sure that if wisper mode was achieved, and only conformation was never recieved,
                                                                        //that the client is returned to Broadcast Mode
                                                                        
                                                                        //creates a new Message object
                                                                        Message lastmsg = new Message();
                                                                        //sets the Messages wisp variable to true which is used to suppress mode change conformation
                                                                        lastmsg.setWisp(true);
                                                                        //make the Message a end wisper command
                                                                        lastmsg.setEndWisp(true);
                                                                        //sends the message to the ClientHandler
                                                                        client.outputMsg( lastmsg );
                                                                        
                                                                        //inform the client that a problem has occured
                                                                        System.out.println("**The attempt to wisper " +toNick + " caused an Unknown Exception (JTECode 1)**" +
                                                                                "\n**Please refer to documentation for possible causes of this exception**\n" +
                                                                                "**Your chat session and all other users have been unaffected**");
                                                                        
                                                                    }
                                                                }
                                                                
                                                                //resets the resultRecieved flag for re-use
                                                                resultRecieved=false;
                                                                
                                                                //if the attempt succeeded (ie the message returned has caused attempt wisp to still be true)
                                                                if (attemptWisp){
                                                                    
                                                                    //reset the attemptWisp flag for re-use
                                                                    attemptWisp=false;
                                                                    
                                                                    
                                                                    //creates a new Message object
                                                                    Message nxtmsg = new Message();
                                                                    //set the content of this message to the provided message (third and proceeding words)
                                                                    nxtmsg.setContent(inpt[2]);
                                                                    //sends the message to the ClientHandler
                                                                    client.outputMsg(nxtmsg);
                                                                    
                                                                    //creates a new Message object
                                                                    Message lastmsg = new Message();
                                                                    //sets the Messages wisp variable to true which is used to suppress mode change conformation
                                                                    lastmsg.setWisp(true);
                                                                    //make the Message a end wisper command
                                                                    lastmsg.setEndWisp(true);
                                                                    //sends the message to the ClientHandler
                                                                    client.outputMsg( lastmsg );
                                                                }
                                                            } 
                                                            else{
                                                                //if the message was a wisper command and nickname with no further text
                                                                //set the Messages MsgTo variable to the provided nickname, acting as a wisper command to the ClientHandler
                                                                sndMsg.setMsgTo(toNick);
                                                                //sends the message to the ClientHandler
                                                                client.outputMsg(sndMsg);
                                                            }
                                                        }
                                                    } 
                                                    else{
                                                        //if the read line is a normal message
                                                        //set the Message content to the inputed text
                                                        sndMsg.setContent(inputedText);
                                                        //sends the message to the ClientHandler
                                                        client.outputMsg( sndMsg );
                                                    }
                                                }
                                            } 
                                            else{
                                                //if the read line is a normal message
                                                //set the Message content to the inputed text
                                                sndMsg.setContent(inputedText);
                                                //sends the message to the ClientHandler
                                                client.outputMsg( sndMsg );
                                            }
                                        } 
                                        else{
                                            //if the read line is a normal message
                                            //set the Message content to the read line
                                            sndMsg.setContent(inputedText);
                                            //sends the message to the ClientHandler
                                            client.outputMsg( sndMsg );
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } 
            catch (IOException ex) {
                //if an error occurs prints the stack trace to assist debugging
                //ex.printStackTrace ();
            }
        }
    }
    
    
   /**
    * Displays the provided message in the GUI
    * Also checks and updates nickname as neccesary.
    * @param msg   Message to be displayed in the GUI
    */  
    public void displayMsg(Message msg){
        //if the user has changes their Nickname
        if (!msg.getNick().equals(client.getNickname())){
               //saves the new nickname to the clients nickname variable
               client.setNickname(msg.getNick());
        }
        
        String line;
        //if the message is marked as a wisper
        if (msg.getWisp()){
            
            //outputs what they have been wispered and by who to the output area of the GUI
            line = (msg.getMsgFrom()+ " Wispers to you - "+ msg.getContent());
        }
        else{
            //if its a normal message
            //outputs the message to the output area of the GUI
            line = msg.getContent();
        }
        
        //if the user is attempting to wisper using the /w <nickname> <message> command
        if (attemptWisp){
            //if the message has been marked as a result of that attempt
            if (msg.getMsgFrom().equals(client.getNickname())){
                //sets the attemptWisp value to that of the messages WispSuccess
                attemptWisp = msg.getWispSuccess();
                //set the resultRecieved flag to true such that the client may procceed with the attemted wisper
                resultRecieved=true;
            }
        }
        
        //if the message is not empty
        if (!line.equals("")){
            //outputs the message to the output area of the GUI
            System.out.println(line);
        }
    }
      
  /**
   * Terminates the thread listening for user input to the CL and closes the 
   * BufferedReader preventing further input.
   * and states that connection with server was lost.
   */
    public void disconnect(){
      //terminates the CLI's thread cleanly
      run=false;
      try {
          //closes the BufferedReader
          input.close();
      } catch (IOException ex) {
          //if an error occurs prints the stack trace to assist debugging
          //ex.printStackTrace();
      }
      //informs the user that they have been disconnected
      System.out.println("**Connection to the Server Lost**\n"); 
    }
    
}
